enum PROMISE_STATUS {
    PENDING = 'PENDING',
    FULFILLED = 'FULFILLED',
    REJECTED = 'REJECTED'
}

export interface IResolve<T> {
    (value: T | ISocketPromise<T>): Function[] | null;
}

export interface IReject{
    (reason?: any): void;
}
//回调执行器
export interface IExecutor<T> {
    (reasolve: IResolve<T>, reject: IReject): void;
}

export interface ISocketPromise<T> {
    then<ReturnType = T, Never = never>(
        onFulfilled?: ((value: T | ISocketPromise<T>) => ReturnType | ISocketPromise<ReturnType>) | undefined | null,
        onRejected?: ((reason: any) => Never | ISocketPromise<Never>) | undefined | null
    ): ISocketPromise<ReturnType | Never>;
    catch<Never = never>(
        errorCallback: ((reason: any) => Never | ISocketPromise<Never>) | undefined | null
    ): ISocketPromise<Never>;
    finally<ReturnType = T, Never = never>(
        finallyCallback: () => any | void
    ): ISocketPromise<ReturnType | Never>
}

/**
 * The Promise Resolution Procedure
 */
function resolvePromise<T>(promise2: ISocketPromise<T> | null, x: T | ISocketPromise<T>, resolve: IResolve<T>, reject: IReject): void{
    // 如果 promise2 和 x 是同一个引用，报TypeError
    // If promise and x refer to the same object, reject promise with a TypeError as the reason.
    if(promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<SocketPromise>'))
    }

    // 如果 resolve、reject 两个同时调用，或者对同一个实参进行多次调用，采用第一个调用，忽略其他的调用
    // If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
    let called = false;

    // Otherwise, if x is an object or function
    if((typeof x === 'object' && x !== null) || typeof x === 'function'){

        try {
            // Let then be x.then
            let then = (x as ISocketPromise<T>).then;

            if(typeof then === 'function') {

                // If then is a function, call it with x as this, first argument resolvePromise, and second argument rejectPromise
                then.call(x, (y: T | ISocketPromise<T>) => {
                    if(called) return;
                    called = true;
                    // If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
                    resolvePromise(promise2, y, resolve, reject);
                }, (r: any) => {
                    // If/when rejectPromise is called with a reason r, reject promise with r.
                    if(called) return;
                    called = true;
                    reject(r);
                });

            } else {
                // 如果 then 不是方法, 我们就认为 x 不是promise, 直接 resolve x
                // If then is not a function, fulfill promise with x.
                resolve(x);
            }
        } catch (e) {
            // 如果 x.then 被拦截，其抛出异常，reject 异常
            // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
            /**
             * Object.defineProperty(x, then, {
             *    get() {
             *       ... throw new Error ...
             *    }
             * })
             */
            if(called) return;
            called = true;
            reject(e);
        }
    } else {
        // 如果 x 不是 object、function，那就是值
        // If x is not an object or function, fulfill promise with x.
        resolve(x);
    }
}

function isPromise(x: any, reject: IReject) {
    if((typeof x === 'object' && x !== null) || typeof x === 'function'){
        try {
            let then = x.then;
            return typeof then === 'function';
        } catch (e) {
            reject(e);
        }
    }
    return false;
}

function isIterable (value: any): boolean {
    return value !== undefined && value !== null && typeof value[Symbol.iterator] === 'function';
}

export default class SocketPromise<T> {

    private status: PROMISE_STATUS = PROMISE_STATUS.PENDING;
    private value!: T | ISocketPromise<T>;
    private reason: any;
    private onFulfilledCallbacks: (() => Function)[] = [];
    private onRejectedCallbacks: (() => void)[] = [];

    constructor(executor: IExecutor<T>){

        const resolve = (value: T | ISocketPromise<T>):(Function[] | null) =>  {
            if(isPromise(value, reject)){
                (value as ISocketPromise<T>).then(resolve, reject);
                return null;
            }

            this.status = PROMISE_STATUS.FULFILLED;
            this.value = value;

            return this.onFulfilledCallbacks.map(fn => fn());
        }


        const reject = (reason: any) => {
            this.status = PROMISE_STATUS.REJECTED;
            this.reason = reason;

            this.onRejectedCallbacks.forEach(fn => fn())
        }

        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }

    /**
     * then 必须返回一个 promise
     * then must return a promise
     *  promise2 = promise1.then(onFulfilled, onRejected);
     *
     * 不管是成功还是失败 都会返回一个 x 【值】
     * If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
     * 如果在成功或失败中 抛出一个异常，promise2就必须reject一个reason
     * If either onFulfilled or onRejected throws an exception e, promise2 must be rejected with e as the reason.
     */
    then<ReturnType = T, Never = never>(
        onFulfilled?: ((value: T | ISocketPromise<T>) => ReturnType | ISocketPromise<ReturnType>) | undefined | null,
        onRejected?: ((reason: any) => Never | ISocketPromise<Never>) | undefined | null
    ): ISocketPromise<ReturnType | Never> {
        /**
         * Both onFulfilled and onRejected are optional arguments:
         If onFulfilled is not a function, it must be ignored.
         If onRejected is not a function, it must be ignored.
         */
        // 如果不是方法, 就直接将值传递下去。
        onFulfilled = typeof onFulfilled === 'function'
            ? onFulfilled
            : (value: T | ISocketPromise<T>) => value as any;
        onRejected = typeof onRejected === 'function'
            ? onRejected
            : (reason: any) => {
                throw reason;
            };

        /**
         * onFulfilled、onRejected 确保是异步执行
         * In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack.
         * 你可以用 setTimeout、setImmediate，也可以用 MutationObserve、process.nextTick
         * This can be implemented with either a “macro-task” mechanism such as setTimeout or setImmediate, or with a “micro-task” mechanism such as MutationObserver or process.nextTick.
         */
        let promise2 = new SocketPromise<ReturnType | Never>((resolve: IResolve<ReturnType | Never>, reject: IReject) => {
            if(this.status === PROMISE_STATUS.FULFILLED){
                onFulfilled && setTimeout(() => {
                    try {
                        let x: any = onFulfilled!(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                }, 0); // => setTimeout >= 4ms
            }

            if(this.status === PROMISE_STATUS.REJECTED){
                onRejected && setTimeout(() => {
                    try {
                        let x: any = onRejected!(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                }, 0);
            }

            // 针对异步与多次调用问题
            // 例如：setTimeout 的回调还未执行, 其status必然还是 pending 状态
            // 那么就需要收集 所有 then 中的 callback
            // 在其状态发生改变后，按顺序执行所有的 callback
            /**
             * then may be called multiple times on the same promise.
             If/when promise is fulfilled, all respective onFulfilled callbacks must execute in the order of their originating calls to then.
             If/when promise is rejected, all respective onRejected callbacks must execute in the order of their originating calls to then.
             */
            if(this.status === PROMISE_STATUS.PENDING){
                // 这里是pending状态，不需要异步
                // 它这里仅仅是 收集callback
                // 内部的函数是在 状态发生变化【resolve、reject】才会执行
                onFulfilled && this.onFulfilledCallbacks.push(()=> {
                    // 这里在pending内部不加也没关系，但还是需要准守规范
                    setTimeout(() => {
                        try {
                            let x: any = onFulfilled!(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                    return onFulfilled as Function;
                });

                onRejected && this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x: any = onRejected!(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                });
            }
        })

        return promise2;
    }

    static resolve<T>(value: T | ISocketPromise<T>): ISocketPromise<T>{
        return new SocketPromise<T>((resolve: IResolve<T>) => {
            resolve(value);
        })
    }

    static reject(error: any): ISocketPromise<never> {
        return new SocketPromise((resolve, reject: IReject) => {
            reject(error);
        })
    }

    static all<T>(promiseArr: ISocketPromise<T>[]) {
        let resArr: any[] = [],
            idx = 0;

        return new SocketPromise((resolve: IResolve<any[]>, reject: IReject) => {
            promiseArr.map((promise: any, index: number) => {
                if(isPromise(promise, reject)){
                    promise.then((res: any) => {
                        formatResArr(res, index, resolve)
                    }, reject)
                } else {
                    formatResArr(promise, index, resolve)
                }
            })
        });

        function formatResArr(value: any, index: number, resolve: IResolve<any[]>): void {
            resArr[index] = value;
            // => 解决数组中 empty 的问题, 你不能采用 resArr.length 来判断
            if(++idx === promiseArr.length){
                resolve(resArr)
            }
        }
    }

    static allSettled<T>(promiseArr: ISocketPromise<T>[]) {
        let resArr: any[] = [],
            idx: number = 0;

        if(!isIterable(promiseArr)){
            throw new TypeError(promiseArr + 'is not iterable (cannot read property Symbol(Symbol.iterator))');
        }

        return new SocketPromise((resolve: IResolve<any[]>, reject: IReject) => {
            if(promiseArr.length === 0){
                resolve([]);
            }

            promiseArr.map((promise: ISocketPromise<T>, index: number) => {
                if(isPromise(promise, reject)){
                    promise.then((value: any) => {
                        formatResArr('fulfilled', value, index, resolve);
                    }, (reason: any) => {
                        formatResArr('rejected', reason, index, resolve);
                    })
                } else {
                    formatResArr('fulfilled', promise, index, resolve);
                }
            })
        })

        function formatResArr (status: string, value: any, index: number, resolve: IResolve<any[]>){
            switch(status) {
                case 'fulfilled':
                    resArr[index] = {
                        status,
                        value
                    }
                    break;
                case 'rejected':
                    resArr[index] = {
                        status,
                        reason: value
                    }
                    break;
                default:
                    break;
            }

            if(++idx === promiseArr.length){
                resolve(resArr);
            }
        }
    }

    static race<T>(promiseArr: ISocketPromise<T>[]): ISocketPromise<T> {
        let called = false;
        return new SocketPromise<T>((resolve: IResolve<T>, reject: IReject) => {
            promiseArr.map((promise: ISocketPromise<T>) => {
                if(isPromise(promise, reject)){
                    promise.then((value: any) => {
                        if(called) return;
                        called = true;
                        resolve(value);
                    }, (reason: any) => {
                        if(called) return;
                        called = true;
                        reject(reason);
                    });
                }else {
                    if(called) return;
                    called = true;
                    resolve(promise);
                }
            });
        })
    }

    catch<Never = never>(
        errorCallback: ((reason: any) => Never | ISocketPromise<Never>)
    ): ISocketPromise<Never> {
        return this.then(null, errorCallback);
    }

    finally<T>(finallyCallback: () => any | void): ISocketPromise<T> {
        return this.then<T>((value: any) => {
            return SocketPromise.resolve(finallyCallback()).then(() => value);
        }, (reason: any) => {
            return SocketPromise.resolve(finallyCallback()).then(() => {
                throw reason;
            });
        })
    }
}
